#include <cppunit/config/SourcePrefix.h>
#include "TestParser.h"
#include <string>

void TestParser::setUp() {
	ast = AST();
	procTable = ProcTable();
	varTable = VarTable();
	follows = Follows();
	parent = Parent();
	uses = Uses();
	modifies = Modifies();
}

void TestParser::tearDown() {
}

CPPUNIT_TEST_SUITE_REGISTRATION( TestParser );

void TestParser::testParser(){
/***********[ SOURCE CODE UNDER TEST]**************
	procedure ABC {
1.		i = 1;
2.		b = 200;
3.		c = a;
4.		while a {
5.			while beta {
6.				oSCar = 1 + beta + tmp;
7.				while tmp{
8.					oSCar = I + k + j1k + chArlie;
				}
9.				while x {
10.					x = x + 1;
11.					while left {
12.						while right {
13.							Romeo = Romeo + 1;
14.							b = 0;
15.							c = delta + l + width + Romeo;
						}
16.						while c {
17.							c = c +1;
						}
18.						x = x + 1;
					}
				}
19.				a = 2;
			}
20.			w = w + 1;
		}
	}
******************************************************/

	Parser parser = Parser(".\\source\\sourcecode.txt", ast, procTable, varTable, follows, parent);
	parser.buildPKB();
	DesignExtractor designExtractor = DesignExtractor(ast);
	designExtractor.createUsesTable(uses);
	designExtractor.createModifiesTable(modifies);


/* test AST correctness */
	
	CPPUNIT_ASSERT_EQUAL_MESSAGE("AST starts with a program node", PROGRAM, ast.getRoot()->getDesignEntity()->getType());
	/* [program]-->[procedures] */
	vector<Node*> procedures = ast.getRoot()->getChildren();
	int noOfProcedure = procedures.size();
	CPPUNIT_ASSERT_EQUAL_MESSAGE("Program with 1 child, i.e. 1 procedure", 1, noOfProcedure);

	/* [procedure]-->[stmtLst]-->[stmts]-->{ASSIGN, ASSIGN, ASSIGN, WHILE} */
	vector<Node*> statements = procedures.at(0)->getChildren().at(0)->getChildren();
	int noOfStmts = statements.size();
	CPPUNIT_ASSERT_EQUAL_MESSAGE("Procedure with a stmtList of 4 statements", 4, noOfStmts);
		CPPUNIT_ASSERT_EQUAL_MESSAGE("first stmt is ASSIGN", ASSIGN, statements.at(0)->getDesignEntity()->getType());
			CPPUNIT_ASSERT_EQUAL_MESSAGE("left child is VARIABLE", VARIABLE, statements.at(0)->getChildren().at(0)->getDesignEntity()->getType());
			CPPUNIT_ASSERT_EQUAL_MESSAGE("left child is CONSTANT", CONSTANT, statements.at(0)->getChildren().at(1)->getDesignEntity()->getType());
		CPPUNIT_ASSERT_EQUAL_MESSAGE("second stmt is ASSIGN", ASSIGN, statements.at(1)->getDesignEntity()->getType());
		CPPUNIT_ASSERT_EQUAL_MESSAGE("third stmt is ASSIGN", ASSIGN, statements.at(2)->getDesignEntity()->getType());
		CPPUNIT_ASSERT_EQUAL_MESSAGE("fourth stmt is WHILE", WHILE, statements.at(3)->getDesignEntity()->getType());
			CPPUNIT_ASSERT_EQUAL_MESSAGE("NodeID of first WHILE should be 12", 12, statements.at(3)->getNodeID());
	
	/* Retrieve and test While loops */
	DesignEntityTable* deTable = AST::getDesignEntityTable();
	vector<Node*> whileNodes = deTable->getWhileDe();
	int noOfWhileStmt = whileNodes.size();
	CPPUNIT_ASSERT_EQUAL_MESSAGE("Total there are 7 while loops", 7, noOfWhileStmt);
		/* 7 while loops with following node ids: [12, 15, 25, 37, 45, 48, 68] */
		CPPUNIT_ASSERT_EQUAL_MESSAGE("first While nodeID is 12", 12, whileNodes.at(0)->getNodeID());
		CPPUNIT_ASSERT_EQUAL_MESSAGE("third While nodeID is 25", 25, whileNodes.at(2)->getNodeID());
		CPPUNIT_ASSERT_EQUAL_MESSAGE("last While nodeID is 68", 68, whileNodes.at(6)->getNodeID());

	/* 14. b = 0; // assign node is on index 56 */
	AST subtree = ast.getSubTree(56);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("Root node should be ASSIGN node", ASSIGN, subtree.getRoot()->getDesignEntity()->getType());
		vector<Node*> children = subtree.getRoot()->getChildren();
		CPPUNIT_ASSERT_EQUAL_MESSAGE("first child is VARIABLE(b)", VARIABLE, children.at(0)->getDesignEntity()->getType());
		CPPUNIT_ASSERT_EQUAL_MESSAGE("second child is CONSTANT(0)", CONSTANT, children.at(1)->getDesignEntity()->getType());
		CPPUNIT_ASSERT_EQUAL_MESSAGE("Constant of value 0", 0, ((Constant*)children.at(1)->getDesignEntity())->getValue());

	CPPUNIT_ASSERT_EQUAL_MESSAGE("There are a total of 89 nodes with last node of nodeID = 88", 88, ast.getSubTree(88).getRoot()->getNodeID());
	CPPUNIT_ASSERT_EQUAL_MESSAGE("nodeID exceeding 88 should be not found", (Node*)0, ast.getSubTree(89).getRoot());
	

/* test ProcTable correctness */
	CPPUNIT_ASSERT_EQUAL_MESSAGE("There is only 1 procedure, i.e. ABC", 1, procTable.size());
	CPPUNIT_ASSERT_EQUAL_MESSAGE("Procedure index start from 0", 0, procTable.getIndex("ABC"));
	vector<int> procNodeIDs = procTable.getNodeIDs(0);
	int noOfProcNodes = procNodeIDs.size();
	CPPUNIT_ASSERT_EQUAL_MESSAGE("Procedure ABC occurs only in 1 node", 1, noOfProcNodes);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("Node ID of ABC is 1, insync with AST", 1, procNodeIDs.at(0));
	

/* test VarTable correctness */
	int xVarIndex = varTable.getIndex("x");
	vector<int> xNodeIDs= varTable.getNodeIDs(xVarIndex);
	
	int xOccurrence = xNodeIDs.size();
	CPPUNIT_ASSERT_EQUAL_MESSAGE("variable 'x' has 5 occurrence", 5, xOccurrence);
		/* 'x' occur on node IDs: [38, 41, 43, 77, 79] */
		CPPUNIT_ASSERT_EQUAL_MESSAGE("first node with ID 38", 38, xNodeIDs.at(0));
		CPPUNIT_ASSERT_EQUAL_MESSAGE("middle node with ID 43", 43, xNodeIDs.at(2));
		CPPUNIT_ASSERT_EQUAL_MESSAGE("last node with ID 79", 79, xNodeIDs.at(4));


/* test Follows correctness */
	vector<pair<int, int>> results = follows.getFollows(1, ANYVALUE);
	int noOfResults = results.size();
	CPPUNIT_ASSERT_EQUAL_MESSAGE("Total 1 result found", 1, noOfResults);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("The only result of (1,2)", 2, results.at(0).second);
	
	results = follows.getFollowsStar(1, ANYVALUE);
	noOfResults = results.size();
	CPPUNIT_ASSERT_EQUAL_MESSAGE("Total 3 results found: (1,2), (1,3), (1,4)", 3, noOfResults);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("last result is (1,4)", 4, results.at(2).second);
	
	results = follows.getFollowsStar(ANYVALUE, 15);
	noOfResults = results.size();
	CPPUNIT_ASSERT_EQUAL_MESSAGE("Total 2 results found: (13,15), (14,15)", 2, noOfResults);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("last result is (13,15)", 13, results.at(0).first);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("last result is (14,15)", 14, results.at(1).first);


/* test Parent correctness */
	results = parent.getParent(ANYVALUE, ANYVALUE);
	noOfResults = results.size();
	CPPUNIT_ASSERT_EQUAL_MESSAGE("Total 16 results found", 16, noOfResults);

	results = parent.getParentStar(11, ANYVALUE);
	noOfResults = results.size();
	CPPUNIT_ASSERT_EQUAL_MESSAGE("Total 7 results found: 11 is a parent* of [12, 13, 14, 15, 16, 17, 18]", 7, noOfResults);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("first result is (11,12)", 12, results.at(0).second);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("last result is (11,18)", 18, results.at(6).second);

	results = parent.getParentStar(ANYVALUE, 17);
	noOfResults = results.size();
	CPPUNIT_ASSERT_EQUAL_MESSAGE("Total 5 results found: [4, 5, 9, 11, 16] are parent* of 17", 5, noOfResults);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("first result is (4,17)", 4, results.at(0).first);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("last result is (16,17)", 16, results.at(4).first);

/* test Uses correctness */
	results = uses.getUsesStatement(8, ANYVALUE);
	noOfResults = results.size();
	CPPUNIT_ASSERT_EQUAL_MESSAGE("Total 4 results, stmtNum 8 uses [I, k, j1k, chArlie]", 4, noOfResults);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("Variable 'I'", varTable.getIndex("I"), results.at(0).second);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("Variable 'k'", varTable.getIndex("k"), results.at(1).second);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("Variable 'j1k'", varTable.getIndex("j1k"), results.at(2).second);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("Variable 'chArlie'", varTable.getIndex("chArlie"), results.at(3).second);

	int varIndex = varTable.getIndex("beta");
	results = uses.getUsesStatement(ANYVALUE, varIndex);
	noOfResults = results.size();
	CPPUNIT_ASSERT_EQUAL_MESSAGE("Total 3 results, stmtNum [4, 5, 6] uses 'beta'", 3, noOfResults);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("first result is stmt 4", 4, results.at(0).first);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("last result is stmt 6", 6, results.at(2).first);

	//results = uses.getUsesProcedure(ANYVALUE, ANYVALUE);
	//printVectorPairs("All procedure Uses raw", results);

/* test Modifies correctness */
	results = modifies.getModifiesStatement(19, ANYVALUE);
	noOfResults = results.size();
	CPPUNIT_ASSERT_EQUAL_MESSAGE("Total 1 result, stmtNum 19 modifies 'a'", 1, noOfResults);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("the only result of variable 'a'", varTable.getIndex("a"), results.at(0).second);
	
	results = modifies.getModifiesStatement(9, ANYVALUE);
	noOfResults = results.size();
	CPPUNIT_ASSERT_EQUAL_MESSAGE("Total 4 result, stmtNum 9 modifies [b, c, x, Romeo]", 4, noOfResults);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("last result of variable 'Romeo'", varTable.getIndex("Romeo"), results.at(3).second);

	results = modifies.getModifiesStatement(ANYVALUE, varTable.getIndex("b"));
	noOfResults = results.size();
	CPPUNIT_ASSERT_EQUAL_MESSAGE("Total 7 result, stmtNum [2, 4, 5, 9, 11, 12, 14] modifies 'b'", 7, noOfResults);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("first result of (2,b)", 2, results.at(0).first);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("fourth result of (9,b)", 9, results.at(3).first);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("last result of (14,b)", 14, results.at(6).first);

	//results = modifies.getModifiesProcedure(ANYVALUE, ANYVALUE);
	//printVectorPairs("All procedure Modifies raw", results);
	deTable->clearAll();
}

void TestParser::printVectorPairs(char* dataOf, vector<pair<int, int>> results){
	printf("%s: ", dataOf);
	for(vector<pair<int,int>>::iterator it = results.begin(); it != results.end(); it++){
		printf("(%d,%d), ", it->first, it->second);
	}
	printf("\n");
}